package cn.edu.hit.core;

import java.util.Optional;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.logging.Logger;
import java.util.stream.Stream;

/**
 * 服务加载器
 *
 * @param <ID>  原数据的ID的类型
 * @param <R>   转换后的结果类型
 * @param <C>   配置类型
 * @param <ARG> 转换函数所需的额外参数的类型
 * @param <A>   适配器接口类型
 */
public abstract class Loader<ID, R, C, ARG, A extends Adapter<ID, ARG, R, C>> {
    protected static Logger logger = Logger.getLogger("Loader");
    protected Set<A> adapters = ConcurrentHashMap.newKeySet();

    /**
     * 初始化 SPI
     *
     * @param tClass 适配器接口类型
     */
    protected Loader(Class<?> tClass) {
        ServiceLoader<?> serviceLoader = ServiceLoader.load(tClass);
        serviceLoader.forEach(adapter -> {
            System.out.println("Loader load " + adapter.getClass().getName());
            adapters.add((A) adapter);
        });
    }

    protected A getAdapter(String type) {
        Optional<A> optional = adapters.stream().filter(adapter -> adapter.accept(type)).findAny();
        if (!optional.isPresent()) {
            logger.info("Cannot find Adapter with type of " + type + ", replace to the default Adapter.");
        }
        return optional.orElse(defaultAdapter());
    }

    /**
     * 适配、转换函数
     *
     * @param configStream 配置定义
     * @param getID
     * @param getAdaptType
     * @param arg          @return 数据转换结果
     */
    public Stream<R> transfer(Stream<C> configStream,
                              Function<C, ID> getID,
                              Function<C, String> getAdaptType,
                              ARG arg) {
        adapters.forEach(Adapter::clear);
        configStream.forEach(c -> {
            ID id = getID.apply(c);
            String type = getAdaptType.apply(c);
            A adapter = this.getAdapter(type);
            adapter.register(id, c);
        });
        return adapters.stream().flatMap(adapter -> adapter.service(arg));
    }

    /**
     * 默认转换器
     *
     * @return 默认转换器
     */
    protected abstract A defaultAdapter();
}
